
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "util.hpp"
#include "level.hpp"
#include "formatter.hpp"
#include "message.hpp"
#include "sink.hpp"
#include "lopper.hpp"
#include <atomic>
#include <cstdarg>
#include <unordered_map>
namespace wmhLog
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, logLevel::value level,
               Formatter::ptr &formatter, std::vector<LogSink::ptr> &sink)
            : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sinks(sink.begin(), sink.end())
        {
        }

        const std::string& name(){return _logger_name;}
    public:
        // 完成日志消息的格式化工作，将消息转化为特定字符串进行落地写入
        void debug(const std::string &file, size_t line, const std::string &fmt,...)
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志格式化
            //  判断当前日志等级是否满足limit_level
            if (logLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            // 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout<<"vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            serialize(logLevel::value::DEBUG,file,line,res);
            free(res);
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志格式化
            //  判断当前日志等级是否满足limit_level
            if (logLevel::value::INFO < _limit_level)
            {
                return;
            }
            // 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout<<"vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            serialize(logLevel::value::INFO,file,line,res);
            free(res);
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志格式化
            //  判断当前日志等级是否满足limit_level
            if (logLevel::value::WARN < _limit_level)
            {
                return;
            }
            // 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout<<"vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            serialize(logLevel::value::WARN,file,line,res);
            free(res);
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志格式化
            //  判断当前日志等级是否满足limit_level
            if (logLevel::value::ERROR < _limit_level)
            {
                return;
            }
            // 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout<<"vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            serialize(logLevel::value::ERROR,file,line,res);
            free(res);
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 通过传入的参数构造出一个日志消息对象，进行日志格式化
            //  判断当前日志等级是否满足limit_level
            if (logLevel::value::FATAL < _limit_level)
            {
                return;
            }
            // 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout<<"vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            serialize(logLevel::value::FATAL,file,line,res);
            free(res);
        }

    protected:

        void serialize(logLevel::value level,const std::string&file,size_t line,char* str)
        {
            // 构造LogMsg对象
            message msg(level,line,file,_logger_name,str);
            // 通过格式化工具对Logmsg进行格式化，得到格式化后的字符串
            std::stringstream ss;
            _formatter->format(ss,msg);
            // 进行日志落地
            logSink(ss.str().c_str(),ss.str().size());
        }
        /*抽象接口完成不同日志气不同的日志落地方式*/
        virtual void logSink(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        std::string _logger_name; // 日志器名称
        // 原子变量，不加锁，提高效率
        std::atomic<logLevel::value> _limit_level; // 默认日志等级
        Formatter::ptr _formatter;                 // 日志格式化管理器
        std::vector<LogSink::ptr> _sinks;          // 日志落地数组
    };

    //同步日志器:直接往磁盘里面写
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, logLevel::value level,
               Formatter::ptr &formatter, std::vector<LogSink::ptr> &sink)
            :Logger(logger_name,level,formatter,sink)
        {}    
    protected:
        //同步日志器将日志直接通过落地模块句柄进行日志落地
        void logSink(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty()) return;
            for(auto& sink:_sinks)//按照落地方式集合遍历写
            {
                sink->log(data,len);
            }
        }
    };

    //异步日志器：往内存中写，里面的线程再写入磁盘
    class AsycLogger: public Logger
    {
    public:
        AsycLogger(const std::string &logger_name, logLevel::value level,
               Formatter::ptr &formatter, std::vector<LogSink::ptr> &sink,
               AsyncSafeType lopper_type)
            :Logger(logger_name,level,formatter,sink),
            _lopper(std::make_shared<AsyncLopper>(std::bind(&AsycLogger::realLog,this,std::placeholders::_1),lopper_type))
        {}
        //将数据写入到缓冲区
        void logSink(const char* data,size_t len)
        {
            _lopper->push(data,len);
        }

        //落地函数：将缓冲区数据落地
        void realLog(Buffer& buf)
        {
            if(_sinks.empty()) return;
            for(auto& sink:_sinks)
            {
                sink->log(buf.begin(),buf.readerAbleSize());
            }
        }
    private:
        AsyncLopper::ptr _lopper;    
    };


/************************************************************************/

    //使用建造者模式来建造日志器
    //抽象一个日志器构造者类，完成日志器对象所需要的零部件构造和日志器构造
    // 设置日志器类型
    // 将不同类型日志器的创建放到同一个日志器建造者类中完成
    enum class LoggerType
    {
        LOGGER_SYNC,//同步日志器
        LOGGER_ASYNC,//异步日志器
    };
    class LoggerBuilder
    {
    public:
        //抽象日志器
        virtual Logger::ptr build()=0;

        LoggerBuilder()
            :_logger_type(LoggerType::LOGGER_SYNC),
            _limit_level(logLevel::value::DEBUG),
            _lopper_type(AsyncSafeType::AsyncSafeType_Safe)
        {}
        void buildLoggerType(LoggerType type){_logger_type=type;}
        void buildEnableUnSafeAsync(){_lopper_type=AsyncSafeType::AsyncSafeType_unSafe;}
        void buildLoggerName(const std::string& name){_logger_name=name;}
        void buildLoggerLevel(logLevel::value level){_limit_level=level;}
        void buildFormatter(const std::string&pattern)
        {
            _formatter=std::make_shared<Formatter>(pattern);
        }
        template<typename SinkType,typename ...Args>
        void buildSink(Args&& ...args)
        {
            LogSink::ptr psink=SinkFactor::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        
    protected:
        AsyncSafeType _lopper_type;//异步日志器独属：安全类型
        LoggerType _logger_type;//日志器类型
        std::string _logger_name; // 日志器名称
        std::atomic<logLevel::value> _limit_level; // 默认日志等级
        Formatter::ptr _formatter;                 // 日志格式化管理器
        std::vector<LogSink::ptr> _sinks;          // 日志落地数组
    };

    //派生出具体的建造者类：局部日志器建造者和全局日志器建造者
    class LocalLoggerBuilder:public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty());//日志器必须有名称
            if(_formatter.get()==nullptr)//日志格式化为空，采用默认的
            {
                _formatter=std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            if(_logger_type==LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsycLogger>(_logger_name,_limit_level,_formatter,_sinks,_lopper_type);
            }
            //同步日志器
            return std::make_shared<SyncLogger>(_logger_name,_limit_level,_formatter,_sinks);
        }
    };

    /******************************************************/

    //日志器管理者类：单例模式
    class LoggerManager
    {
    public:
        static LoggerManager& getInstance()
        {
            //C++11之后，静态局部变量，编译器在编译层面实现了线程安全
            //局部变量没有构造完成之前，其他线程进入就会阻塞
            static LoggerManager eton;
            return eton;
        }

        void addLogger(Logger::ptr& logger)
        {
            if(SeekLogger(logger->name())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(),logger));
        }

        bool SeekLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it=_loggers.find(name);
            if(it==_loggers.end()) return false;

            return true;
        }

        Logger::ptr GetLogger(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it=_loggers.find(name);
            if(it==_loggers.end()) return Logger::ptr();

            return it->second;
        }
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }
    private:
        LoggerManager()
        {
            //构造的时候只能用LoggerBuilder，这是因为全局日志器需要在局部基础上添加到hash
            //获取单例对象要构造，如果是全局的，就会陷入死循环
            std::unique_ptr<wmhLog::LoggerBuilder> builder(new wmhLog::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger=builder->build();
            _loggers.insert(std::make_pair("root",_root_logger));
        }
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;//默认日志器
        std::unordered_map<std::string,Logger::ptr> _loggers;
    };

    //设计一个全局日志器建造者，在局部的基础上增加一个功能
    //将日志器添加到单例对象中
    class GlobalLoggerBuilder:public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty());//日志器必须有名称
            if(_formatter.get()==nullptr)//日志格式化为空，采用默认的
            {
                _formatter=std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if(_logger_type==LoggerType::LOGGER_ASYNC)
            {
                logger= std::make_shared<AsycLogger>(_logger_name,_limit_level,_formatter,_sinks,_lopper_type);
            }
            else
            {    //同步日志器
                logger=std::make_shared<SyncLogger>(_logger_name,_limit_level,_formatter,_sinks);
            }

            LoggerManager::getInstance().addLogger(logger);
            return logger;
        }
    };
}

#endif